#include "File.h"
#include <iostream>
#include "FileSystem.h"


static const char* openMode[] =
{
	"rb",
	"wb",
	"r+b",
	"w+b"
};

File::File() :mode_(FILE_READ),
handle_(nullptr),
size_(0),
position_(0)
{

}

File::File(const char* fileName, FileMode mode) :fileName_(fileName),
mode_(mode),
handle_(nullptr),
size_(0),
position_(0)
{

}


File::~File()
{
	Close();
}

unsigned File::Read(void* dest, unsigned size)
{
	if (!IsOpen())
	{
		return 0;
	}

	if (mode_ == FILE_WRITE)
	{
		return 0;
	}

	if (size + position_ > size_)
		size = size_ - position_;
	if (!size)
		return 0;

	if (!ReadInternal(dest, size))
	{
		SeekInternal(position_);
		std::cout << "Error while reading from file " + GetName() << std::endl;
		return 0;
	}

	position_ += size;
	return size;
}

unsigned File::Seek(unsigned position)
{
	if (!IsOpen())
	{
		return 0;
	}

	if (mode_ == FILE_READ && position > size_)
		position = size_;

	SeekInternal(position);
	position_ = position;
	return position_;
}
unsigned File::Write(const void* data, unsigned size)
{
	if (!IsOpen())
	{
		// If file not open, do not log the error further here to prevent spamming the stderr stream
		return 0;
	}

	if (mode_ == FILE_READ)
	{
		std::cout << "File not opened for writing" << std::endl;
		return 0;
	}

	if (!size)
		return 0;


	if (fwrite(data, size, 1, (FILE*)handle_) != 1)
	{
		// Return to the position where the write began
		fseek((FILE*)handle_, (long)position_, SEEK_SET);
		std::cout << "Error while writing to file " + GetName() << std::endl;
		return 0;
	}

	position_ += size;
	if (position_ > size_)
		size_ = position_;

	return size;
}
bool File::Open(FileMode mode)
{
	Close();

	if (fileName_.length() == 0)
	{
		return false;
	}

	handle_ = fopen(GetNativePath(fileName_).c_str(), openMode[mode]);

	if (mode == FILE_READWRITE && !handle_)
	{
		handle_ = fopen(GetNativePath(fileName_).c_str(), openMode[mode + 1]);
	}

	if (!handle_)
	{
		return false;
	}

	mode_ = mode;
	position_ = 0;

	fseek((FILE*)handle_, 0, SEEK_END);
	long size = ftell((FILE*)handle_);
	fseek((FILE*)handle_, 0, SEEK_SET);
	if (size > 0xffffffff)
	{
		Close();
		size_ = 0;
		return false;
	}
	size_ = (unsigned)size;

	return true;
}
void File::Close()
{
	if (handle_)
	{
		fclose((FILE*)handle_);
		handle_ = nullptr;
		position_ = 0;
		size_ = 0;
	}
}
void File::Flush()
{
	if (handle_)
		fflush((FILE*)handle_);
}
bool File::IsOpen() const
{
	return handle_ != nullptr;
}
bool File::ReadInternal(void* dest, unsigned size)
{
	return fread(dest, size, 1, (FILE*)handle_) == 1;
}
void File::SeekInternal(unsigned newPosition)
{
	fseek((FILE*)handle_, newPosition, SEEK_SET);
}

signed char File::ReadByte()
{
	signed char ret;
	Read(&ret, sizeof ret);
	return ret;
}

std::string File::ReadLine()
{
	std::string ret;

	while (!IsEof())
	{
		char c = ReadByte();
		if (c == 10)
			break;
		if (c == 13)
		{
			// Peek next char to see if it's 10, and skip it too
			if (!IsEof())
			{
				char next = ReadByte();
				if (next != 10)
					Seek(position_ - 1);
			}
			break;
		}

		ret += c;
	}

	return ret;
}
std::string File::ReadString()
{
	std::string ret;

	while (!IsEof())
	{
		char c = ReadByte();
		ret += c;
	}
	return ret;
}

